package com.yxw.live_vod_boot.utils;

import com.yxw.yxnet_cd_center.common.constant.CommonConstant;
import com.yxw.yxnet_cd_center.common.enums.CommonErrorEnum;
import com.yxw.yxnet_cd_center.common.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.integration.redis.util.RedisLockRegistry;

import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.Collection;
import java.util.List;
import javax.annotation.Resource;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ObjectUtil;
import java.util.LinkedHashMap;
import java.util.Set;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
/**
 * redis管理操作
 * @author Administrator
 */
@Slf4j
@Component
public class RedisManager {

    @Resource
    private RedisTemplate redisTemplate;
    


    /**
     * 存入redis 带过期时间
     * @param key 键
     * @param value 值
     * @param time  过期时间，单位秒
     */
    public void set(String key, Object value, long time) {
        try {
            redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("带过期时间存入redis数据异常：{}", e);
            BaseException exception = new BaseException(CommonErrorEnum.REDIS_ERROR);
            exception.setPrimaryErrorCode(CommonConstant.SYSTEM_NAME + CommonConstant.PLUS
                    + CommonErrorEnum.REDIS_ERROR.getCode());
            exception.setPrimaryErrorMsg(CommonErrorEnum.REDIS_ERROR.getDescription());
            exception.setPrimaryErrorIP(IpUtil.getIp());
            throw exception;
        }

    }

    /**
     * 存入redis 不带过期时间
     * @param key 键
     * @param value 值
     */
    public void set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
        } catch (Exception e) {
            log.error("存入redis数据异常：{}", e);
            BaseException exception = new BaseException(CommonErrorEnum.REDIS_ERROR);
            exception.setPrimaryErrorCode(CommonConstant.SYSTEM_NAME + CommonConstant.PLUS
                    + CommonErrorEnum.REDIS_ERROR.getCode());
            exception.setPrimaryErrorMsg(CommonErrorEnum.REDIS_ERROR.getDescription());
            exception.setPrimaryErrorIP(IpUtil.getIp());
            throw exception;
        }
    }
    public boolean exsist(String key)
    {
        return redisTemplate.hasKey(key);
    }
    public boolean exsistInHash(String key,String hashKey)
    {
        return redisTemplate.opsForHash().hasKey(key,hashKey);
    }
    /**
     * 获取redis存入值
     */
    public Object get(String key) {
        try
        {
            
            return redisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            log.error("redis获取异常：{}", e);
            BaseException exception = new BaseException(CommonErrorEnum.REDIS_ERROR);
            exception.setPrimaryErrorCode(CommonConstant.SYSTEM_NAME + CommonConstant.PLUS
                    + CommonErrorEnum.REDIS_ERROR.getCode());
            exception.setPrimaryErrorMsg(CommonErrorEnum.REDIS_ERROR.getDescription());
            exception.setPrimaryErrorIP(IpUtil.getIp());
            throw exception;
        }
    }
    /**
     * 获取redis存入值
     */
    public  <T>T getT(String key) {
        try
        {
            ValueOperations<String, T> valueOperations = redisTemplate.opsForValue();
            return valueOperations.get(key);           
            //return redisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            log.error("redis获取异常：{}", e);
            BaseException exception = new BaseException(CommonErrorEnum.REDIS_ERROR);
            exception.setPrimaryErrorCode(CommonConstant.SYSTEM_NAME + CommonConstant.PLUS
                    + CommonErrorEnum.REDIS_ERROR.getCode());
            exception.setPrimaryErrorMsg(CommonErrorEnum.REDIS_ERROR.getDescription());
            exception.setPrimaryErrorIP(IpUtil.getIp());
            throw exception;
        }
    }    
    /**
     * 获取redis存入值
     */
    public List<Object> multiGet(List<String> keys) {
        try
        {
            return redisTemplate.opsForValue().multiGet(keys);
        } catch (Exception e) {
            log.error("redis获取异常：{}", e);
            BaseException exception = new BaseException(CommonErrorEnum.REDIS_ERROR);
            exception.setPrimaryErrorCode(CommonConstant.SYSTEM_NAME + CommonConstant.PLUS + CommonErrorEnum.REDIS_ERROR.getCode());
            exception.setPrimaryErrorMsg(CommonErrorEnum.REDIS_ERROR.getDescription());
            exception.setPrimaryErrorIP(IpUtil.getIp());
            throw exception;
        }
    }
    /**
     * 删除 key
     * @param key 键
     * @return Boolean
     */
    public Boolean del(String key) {
        try {
            return redisTemplate.delete(key);
        } catch (Exception e) {
            log.error("redis删除key异常：{}", e);
            BaseException exception = new BaseException(CommonErrorEnum.REDIS_ERROR);
            exception.setPrimaryErrorCode(CommonConstant.SYSTEM_NAME + CommonConstant.PLUS
                    + CommonErrorEnum.REDIS_ERROR.getCode());
            exception.setPrimaryErrorMsg(CommonErrorEnum.REDIS_ERROR.getDescription());
            exception.setPrimaryErrorIP(IpUtil.getIp());
            throw exception;
        }
    }
    /**
     * 批量存入redis
     *
     * @param valueMap
     */    
    public void multiSet(Map<String,Object> valueMap)
    {
        /*
        Map valueMap = new HashMap<String,Object>();
        map.forEach((key, value) -> {
            valueMap.put(key, value);
        });  
         */
        try
        {
            redisTemplate.opsForValue().multiSet(valueMap);
        } 
        catch (Exception e) {
            log.error("批量存入redis数据异常：{}", e);
            BaseException exception = new BaseException(CommonErrorEnum.REDIS_ERROR);
            exception.setPrimaryErrorCode(CommonConstant.SYSTEM_NAME + CommonConstant.PLUS
                    + CommonErrorEnum.REDIS_ERROR.getCode());
            exception.setPrimaryErrorMsg(CommonErrorEnum.REDIS_ERROR.getDescription());
            exception.setPrimaryErrorIP(IpUtil.getIp());
            throw exception;
        }        
    }

    /**
     * set存入
     * 存入redis 不带过期时间
     * @param key 键
     * @param value 值
     */
    public void addBySet(String key, Object value) {
        try {
            redisTemplate.opsForSet().add(key,value);
        } catch (Exception e) {
            log.error("redis存入异常：====================================>{}", e);
            throw new BaseException(CommonErrorEnum.REDIS_ERROR,e.getMessage());
        }
    }

    /**
     * key是否存在于集合中
     * @param key
     */
    public Boolean existBySet(String key,Object value) {
        try {
           return  redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            log.error("redis查询集合异常：====================================>{}", e);
            throw new BaseException(CommonErrorEnum.REDIS_ERROR,e.getMessage());
        }
    }

    /**
     * set集合删除
     * @param key
     */
    public void delBySet(String key,Object value) {
        try {
           redisTemplate.opsForSet().remove(key,value);
        } catch (Exception e) {
            log.error("redis删除集合异常：====================================>{}", e);
            throw new BaseException(CommonErrorEnum.REDIS_ERROR,e.getMessage());
        }
    }

    /**
     * decrBy命令
     * @param key
     * @param decrement
     * @return
     */
    public void decrBy(String key, long decrement) {
        try {
            redisTemplate.opsForValue().decrement(key, decrement);
        } catch (Exception e) {
            log.error("redis减少值命令异常：====================================>{}", e);
            throw new BaseException(CommonErrorEnum.REDIS_ERROR, e.getMessage());
        }
    }

    /**
     * incrBy命令
     * @param key
     * @param increment
     * @return
     */
    public void incrBy(String key, long increment) {
        try {
            redisTemplate.opsForValue().increment(key, increment);
        } catch (Exception e) {
            log.error("redis增加值命令异常：====================================>{}", e);
            throw new BaseException(CommonErrorEnum.REDIS_ERROR, e.getMessage());
        }
    }
    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @return true=设置成功；false=设置失败
     */
    public boolean expire(final String key, final long timeout)
    {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功；false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit)
    {
        return redisTemplate.expire(key, timeout, unit);
    }    
    
    /**
     * 缓存HashMap
     *
     * @param key
     * @param dataMap
     */
    public <T> void setMap(final String key, final Map<String, T> dataMap)
    {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }

    /**
     * 获得缓存的HashMap
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getMap(final String key)
    {  
        return redisTemplate.opsForHash().entries(key);
    }
    /**
     * 删除Hash
     * 
     * @param key
     */
    public void delMap(final String key)
    {
        redisTemplate.opsForHash().delete(key);
        //HashOperations hashOperations = redisTemplate.opsForHash();
        //hashOperations.delete(key);
    }
    /**
     * 往HashMap中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    public <T> void setMapValue(final String key, final String hKey, final T value)
    {
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    /**
     * 获取Hash中的数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getMapValue(final String key, final String hKey)
    {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }
    /**
     * 获取多个HashMap中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiMapValue(final String key, final Collection hKeys)
    {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }
    /**
     * 删除Hash中的数据
     * 
     * @param key
     * @param mapkey
     */
    public void delMapValue(final String key, final String mapkey)
    {
        redisTemplate.opsForHash().delete(key, mapkey);
        //HashOperations hashOperations = redisTemplate.opsForHash();
        //hashOperations.delete(key, mapkey);
    }
    /**
     * 缓存List数据
     *
     * @param key 缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setList(final String key, final List<T> dataList)
    {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }

    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getList(String key)
    {
        return redisTemplate.opsForList().range(key, 0, -1);
    }
    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getList(String key,Long start,Long end)
    {
        Long size = redisTemplate.opsForList().size(key);
        if(size.intValue() == 0)
        {
            return null;
        }
        size = size -1;
        if(end > size)
        {
            end = size;
        }
        if(end < 1L)
        {
            end = 1L;
        }        
        if(start >= end)
        {
            start = end - 1;
        }
        if(start < 0L)
        {
            start = 0L;
        }
        return redisTemplate.opsForList().range(key, start, end);
    }
    /**
     * 获得缓存的list对象的长度
     *
     * @param key 缓存的键值
     * @return 缓存键值对应数据的长度
     */
    public Long getListSize(String key)
    {
        return redisTemplate.opsForList().size(key);
    } 
    /**
     * 获得缓存list中的Page对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> Page<T> getPageFromList(String key,Long currPage,Long pageSize)
    {
        if(pageSize < 1L)
        {
            pageSize = 1L;
        }
        if(currPage < 1L)
        {
            currPage = 1L;
        }
        Page<T> pageParams = new Page<>(currPage,pageSize);
        Long size = redisTemplate.opsForList().size(key);
        if(size == 0L)
        {
            pageParams.setTotal(0);
            return pageParams;
        }        
        Long pages = (size%pageSize) == 0L ? size/pageSize: size/pageSize + 1L;
        if(currPage > pages)
        {
            currPage = pages;
        }
        pageParams.setTotal(size);
        pageParams.setCurrent(currPage);
        Long start = (currPage - 1) * pageSize;
        Long end;
        if(currPage.equals(pages))
        {
            end = size - 1;
        }
        else
        {
            end = start + pageSize - 1;
        }
        pageParams.setRecords(redisTemplate.opsForList().range(key, start, end));
        return pageParams;
    }
    /*
    批量写入，并带过期时间
    */
    public void multiSaveByPipe(Map<String, Object> source,Long time) {
        redisTemplate.executePipelined((RedisCallback<Object>) connection ->
        {
            // 这里逻辑简单不会抛异常
            // 否则需要加上try...catch...finally防止链接未正常关闭 造成泄漏
            connection.openPipeline();
            try
            {
                source.forEach((key, value) -> {
                    // hset zset都是可以用的，但是要序列化
                    connection.set(redisTemplate.getKeySerializer().serialize(key),redisTemplate.getValueSerializer().serialize(value));
                    if(time > 0)
                    {
                        connection.expire(redisTemplate.getKeySerializer().serialize(key), time);
                    }
                });
            }
            catch(Exception e)
            {
                log.error("multiSaveByPipe命令异常:====================================>{}", e);
                throw new BaseException("multiSaveByPipe命令异常:"+e.getMessage());
            }
            finally
            {
                connection.close();
            }
            // executePipelined源码要求RedisCallback必须返回null，否则抛异常
            return null;
        });
    } 
    //RedisPipeAccessObject
    /*
    批量操作：(value,Hash)仅限set和del
    */
    public void multiAccessByPipe(List<RedisPipeAccessObject> sourceList) {
        if(sourceList.isEmpty())
        {
            return;
        }
        redisTemplate.executePipelined((RedisCallback<Object>) new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                // 这里逻辑简单不会抛异常
                // 否则需要加上try...catch...finally防止链接未正常关闭 造成泄漏
                connection.openPipeline();
                try
                { 
                    for(RedisPipeAccessObject oRedisPipeAccessObject : sourceList)
                    {
                        if(StrUtil.isBlank(oRedisPipeAccessObject.getRedisKey()))
                        {
                            continue;
                        }
                        //操作类型：0 put，1 delete,2只设置超时时间
                        if(oRedisPipeAccessObject.getOperationType() == 0)
                        {
                            if(ObjectUtil.isEmpty(oRedisPipeAccessObject.getRedisValue()))
                            {
                                continue;
                            }
                            //数据类型：0 普通值类型，1 hash类型
                            if(oRedisPipeAccessObject.getRedisValueType() == 0)
                            {
                                connection.set(redisTemplate.getKeySerializer().serialize(oRedisPipeAccessObject.getRedisKey()),redisTemplate.getValueSerializer().serialize(oRedisPipeAccessObject.getRedisValue()));
                            }
                            else if(oRedisPipeAccessObject.getRedisValueType() == 1)
                            {
                                if(StrUtil.isBlank(oRedisPipeAccessObject.getRedisInnerHashMapKey()))
                                {
                                    HashMap oHashMap = (HashMap)oRedisPipeAccessObject.getRedisValue();
                                    if(!oHashMap.isEmpty())
                                    {
                                        Map<byte[], byte[]> oMap = new HashMap();                                     
                                        oHashMap.forEach((k,v)->{
                                            oMap.put(redisTemplate.getHashKeySerializer().serialize(k), redisTemplate.getHashValueSerializer().serialize(v));
                                        });
                                        connection.hMSet(redisTemplate.getKeySerializer().serialize(oRedisPipeAccessObject.getRedisKey()),oMap);
                                    }
                                }
                                else
                                {                                    
                                    connection.hSet(redisTemplate.getKeySerializer().serialize(oRedisPipeAccessObject.getRedisKey()), redisTemplate.getHashKeySerializer().serialize(oRedisPipeAccessObject.getRedisInnerHashMapKey()), redisTemplate.getHashValueSerializer().serialize(oRedisPipeAccessObject.getRedisValue()));
                                }
                                
                            }
                            if(oRedisPipeAccessObject.getTime() > 0)
                            {
                                connection.expire(redisTemplate.getKeySerializer().serialize(oRedisPipeAccessObject.getRedisKey()), oRedisPipeAccessObject.getTime());
                            }
                        }
                        else if(oRedisPipeAccessObject.getOperationType() == 1)
                        {
                            if(StrUtil.isBlank(oRedisPipeAccessObject.getRedisInnerHashMapKey()))
                            {
                                connection.del(redisTemplate.getKeySerializer().serialize(oRedisPipeAccessObject.getRedisKey()));                               
                            }
                            else
                            {
                                connection.hDel(redisTemplate.getKeySerializer().serialize(oRedisPipeAccessObject.getRedisKey()), redisTemplate.getHashKeySerializer().serialize(oRedisPipeAccessObject.getRedisInnerHashMapKey()));
                            }
                        }
                        else
                        {
                            if(oRedisPipeAccessObject.getTime() > 0)
                            {
                                connection.expire(redisTemplate.getKeySerializer().serialize(oRedisPipeAccessObject.getRedisKey()), oRedisPipeAccessObject.getTime());
                            }
                            else
                            {
                                connection.expire(redisTemplate.getKeySerializer().serialize(oRedisPipeAccessObject.getRedisKey()), -1);
                            }
                        }
                    }

                }
                catch(Exception e)               
                {
                    log.error("multiSaveByPipe命令异常:====================================>{}", e);
                    throw new BaseException("multiSaveByPipe命令异常:"+e.getMessage());
                }
                finally
                {
                    connection.close();
                }
                // executePipelined源码要求RedisCallback必须返回null，否则抛异常
                return null;
            }
        });
    }     
    
}